package com.xiaodou.mjf.application.basic.auth;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.stereotype.Component;

import com.xiaodou.mjf.application.basic.service.OperatorService;
import com.xiaodou.mjf.application.basic.utils.OperatorUtils;
import com.xiaodou.mjf.entity.basic.Menu;
import com.xiaodou.mjf.entity.basic.Operator;
import com.xiaodou.mjf.entity.basic.Role;
import com.xiaodou.mjf.infrastructure.common.utils.Encodes;
import com.xiaodou.mjf.infrastructure.common.utils.Encrypt;
import com.xiaodou.mjf.infrastructure.common.utils.StringUtils;

/***
 * 授权认证实现类
 * @author Rocky
 *
 */
@Component("authorizingRealm")
public class AuthorizingRealm extends org.apache.shiro.realm.AuthorizingRealm {

	@Resource
    OperatorService operatorService;
	
	/**
	 * 认证回调函数，登录时调用
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken)authcToken;
		
		//TODO 暂时不做验证码校验 
		
		//用户认证
		Operator operator = operatorService.findByLoginName(token.getUsername());
		if(operator == null){
			return null;
		}
		
		byte[] salt = Encodes.decodeHex(operator.getPassword().substring(0, 16));
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(new Principal(operator),operator.getPassword().substring(16),ByteSource.Util.bytes(salt),getName());
		return info;
	}
	
	
	/**
	 * 授权查询回调函数，进行鉴权但缓存中无用户授权信息时调用
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

		Principal principal = (Principal) getAvailablePrincipal(principals);
		
		Operator operator = operatorService.findByLoginName(principal.getLoginName());
		if(operator == null){
			return null;
		}
		
		OperatorUtils.putCache(OperatorUtils.CACHE_OPERATOR, operator);

		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		List<Menu> list = OperatorUtils.getMenuList();
//		List<Menu> list = Lists.newArrayList();
		for(Menu menu : list){
			if(StringUtils.isNotBlank(menu.getPermission())){
				for(String permission : StringUtils.split(menu.getPermission(),",")){
					info.addStringPermission(permission);
				}
			}
		}

        for(Role role : operator.getRoles()) {
            info.addRole(role.getCode());
        }

		return info;
	}
	
	/**
	 * 设定密码校验的Hash算法与迭代次数
	 */
	@PostConstruct
	public void initCredentialsMatcher() {
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(Encrypt.HASH_ALGORITHM);
		matcher.setHashIterations(Encrypt.HASH_INTERATIONS);
		setCredentialsMatcher(matcher);
	}
	
	/**
	 * 清空用户关联权限认证，待下次使用时重新加载
	 */
	public void clearCachedAuthorizationInfo(String principal) {
		SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
		clearCachedAuthorizationInfo(principals);
	}

	/**
	 * 清空所有关联认证
	 */
	public void clearAllCachedAuthorizationInfo() {
		Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
		if (cache != null) {
			for (Object key : cache.keys()) {
				cache.remove(key);
			}
		}
	}
	

	/**
	 * 授权用户信息
	 */
	public static class Principal implements Serializable {

		private static final long serialVersionUID = 1L;
		
		private String id;
		private String loginName;
		private String name;
		private Map<String, Object> cacheMap;

		public Principal(Operator operator) {
			this.id = operator.getId();
			this.loginName = operator.getLoginName();
			this.name = operator.getRealName();
		}

		public String getId() {
			return id;
		}

		public String getLoginName() {
			return loginName;
		}

		public String getName() {
			return name;
		}

		public Map<String, Object> getCacheMap() {
			if (cacheMap==null){
				cacheMap = new HashMap<String, Object>();
			}
			return cacheMap;
		}

	}

}
